#include "N2Lib.h"
#include "N1Lib.h"
#include <windows.h>
#include <wchar.h>
#include <tchar.h>

#define UNICODE
#define _UNICODE

static const wchar_t* gN2WindowClassName = L"N2WindowClass";

N2Window::N2Window(uint32 width, uint32 height) : m_borderSize(1), m_captionSize(16), 
                                                  m_contentView(NULL)
{
    m_hWnd = CreateWindowW(gN2WindowClassName, L"", WS_VISIBLE|WS_POPUP,
                          0, 0, width, height, NULL, NULL, NULL, this);
    if(!m_hWnd)
    {
        N1Log(N1Log_Error, "N2Window: CreateWindowW returned with %d", GetLastError());
    }
}

N2Window::~N2Window()
{
    CloseWindow(m_hWnd);

    if(m_contentView)
    {
        m_contentView->release();
    }
}

N1NAME_DEF(N2Window)

N2Size N2Window::getSize() const 
{
    RECT rect;
    ::GetWindowRect(m_hWnd, &rect);
    return N2Rect(rect).size();
}

N2Size N2Window::getClientSize() const 
{
    N2Size size = getSize();
    size.change(-(2 * m_borderSize), -( 2 * m_borderSize + m_captionSize) );
    return size;
}

uint32  N2Window::getBorderSize() const 
{
    return m_borderSize;
}

uint32  N2Window::getCaptionSize() const 
{
    return m_captionSize;
}

void N2Window::setContentView(N2View* view)
{
    if(m_contentView==view) return;

    if(m_contentView)
    {
        m_contentView->release();
    }

    m_contentView = view;

    if(m_contentView)
    {
        m_contentView->retain();
    }
}

N2View* N2Window::getContentView()
{
    return m_contentView;
}

void N2Window::runLoop(double seconds)
{
    N1AutoreleasePool pool;

    N1Date *untilDate = N1Date::dateWithTimeIntervalSinceNow(seconds);

    MSG msg;

    N1AutoreleasePool p2;
    uint32 counter = 0;
    int32 ret;

    while((ret = GetMessage(&msg, NULL, 0, 0))!=0)
    {
        if(ret==-1)
        {
            break;
        }

        TranslateMessage(&msg);
        DispatchMessage(&msg);

        if((++counter % 100)==0) p2.clear();

        if(*untilDate < *N1Date::today())
        {
            break;
        }
    }
}

void N2Window::runLoop()
{
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        DispatchMessage(&msg);
    }
}

void N2Window::draw(N2Drawer *drawer) 
{
    N2Size windowSize = getSize();
    N2Rect allWindow = N2Rect( windowSize );
    uint32 captionSize = getCaptionSize();

    drawer->drawRect(allWindow);

    if (captionSize)
    {
        drawer->drawHorzLine(captionSize - 1, 0, windowSize.width - 1);
    }

    if(m_contentView)
    {
        N2WorldTransform  wt = drawer->getWorldTransform();

        wt.setOffset(getBorderSize(), captionSize);

        drawer->setWorldTransform(wt);

        m_contentView->draw(drawer);
    }
}

#define N2WINDOW_OBJECT_PTR_INDEX 0

static LRESULT CALLBACK N2WindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
    switch(Msg)
    {
        case WM_CREATE:
            SetLastError(0);
            CREATESTRUCT *cs = (LPCREATESTRUCT)lParam;
            if(!SetWindowLongPtr(hWnd, N2WINDOW_OBJECT_PTR_INDEX, (LONG)cs->lpCreateParams) && GetLastError())
            {
                N1Log(N1Log_Error, "N2Window: SetWindowLongPtr failed with %u", GetLastError());
            }
            break;
        case WM_PAINT: 
            {
                RECT rect;
                PAINTSTRUCT paint;
                if(GetUpdateRect(hWnd, &rect, FALSE))
                {
                    HDC dc = BeginPaint(hWnd, &paint);
                    if(!dc)
                    {
                        N1Log(N1Log_Error, "N2Window: BeginPaint returned NULL");
                        break;
                    }
                    N2Window* window = static_cast<N2Window*>((void*)GetWindowLongPtr(hWnd, N2WINDOW_OBJECT_PTR_INDEX));

                    N2Win32Drawer drawer(dc); 

                    window->draw(&drawer);

                    EndPaint(hWnd, &paint);
                }
            }
            break;
        case WM_ERASEBKGND:
            break;
    }
    return DefWindowProc(hWnd, Msg, wParam, lParam);
}

void N2WindowInitialize()
{
    WNDCLASSEXW wc;

    memset(&wc, 0, sizeof(wc));

    wc.cbSize        = sizeof(wc);
    wc.lpfnWndProc   = (WNDPROC)&N2WindowProc;
    wc.style         = CS_GLOBALCLASS;
    wc.hInstance     = NULL;
    wc.hIcon         = NULL;
    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wc.lpszClassName = gN2WindowClassName;
    wc.cbWndExtra    = sizeof(N2Window*);

    ATOM cls = RegisterClassExW(&wc);
    if(!cls)
    {
        N1Log(N1Log_Error, "N2Window: RegisterClassExW returned with %d", GetLastError());
    }
}
